<?php
namespace Core;

use App\Enums\ErrorEnums;

require 'Const.php';

class Kernel{

    use Singleton;

    /**
     * 处理请求
     *
     * @throws \Exception
     * @author lichunguang 153102250@qq.com
     * @since 2022/4/24 上午9:07
     */
    public function handle(){
        //加载配置文件
        $this->loadConfig();
        //加载路由缓存
        $this->loadRoute();
        //获取路由对应的URL
        $routeUri = $this->getRouteUri();
        //匹配路由, 并设置 module controller 的上下文
        try {
            $matchResult = Route::matchUrl($routeUri);
            if (empty($matchResult)) {
                //dd('未找到定义的路由');
                //查找默认路由, 并设置 module controller 的上下文
                $matchResult = $this->getDefaultRouteArgs($routeUri);
            }
        }catch (\Exception $e){
            $this->errorPage('error', $e->getMessage());
            return false;
        }
        //检查请求方式是否支持
        if($this->checkMethod($matchResult) === false){
            return false;
        }

        //dd($matchResult);
        //这里是重点, 我们把目标方法 转换成一个 闭包方法
        $next = function ($request) use ($matchResult) {
            //转发路由
            return $this->dispatchRoute($matchResult, $request);
        };

        //实例化request, 并获取到所有的请求参数
        $request = Request::getInstance();
        //处理中间件
        return $this->checkMiddleware($request, $next, $matchResult['middleware']);
    }

    /**
     * 检查请求方式
     *
     * @author lichunguang 153102250@qq.com
     * @since 2022/9/15 上午11:03
     */
    public function checkMethod($args){
        if($args['method'] == 'any'){
            return true;
        }
        $request = Request::getInstance();
        if($request->method() != $args['method']){
            $this->errorPage('route', '请求方式不支持，请检查路由');
            return false;
        }
        return true;
    }

    /**
     * 检查中间件
     *
     * @param $request
     * @param  \Closure  $next
     * @param $middleware_vars
     * @author lichunguang 153102250@qq.com
     * @since 2022/8/23 下午12:02
     */
    function checkMiddleware($request, \Closure $next, $middleware_vars){
        //中间件的配置
        $middleware = config('middleware');
//        print_r($middleware_vars);exit;
        if(empty($middleware_vars)){
            return $next($request);
        }
        //实例化中间件
        $middleware_list = [];
        foreach ($middleware_vars as $v){
            $namespace = $middleware[$v];
            //不存在的中件简，会跳过
            if(class_exists($namespace)){
                $middleware_list[] = new $namespace();
            }
        }

        try {
            foreach ($middleware_list as $m) {
                //闭包的嵌套调用
                $next = function ($request) use ($m, $next) {
                    return $m->handle($request, $next);
                };
            }
            return $next($request);
        }catch (\Exception $e){
            log_exception($e);
            $this->errorPage('error', $e->getMessage());
        }
    }

    /**
     * 获取解析路由用的URI home/article/index
     *
     * @return false|string
     * @author lichunguang 153102250@qq.com
     * @since 2022/8/23 上午9:14
     */
    public function getRouteUri(){
        //开启URL重写的情况，URI要取index.php? 或者 ？之后的
        if (config('route.url_rewrite')) {
            $uri = Request::getInstance()->uri();
            $uri = str_replace('/index.php', '', $uri);
            $route = trim($uri, '/');
        } else {//未开启URL重写的情况，URI要取index.php? 或者 ？之后的 &之前的，&之后为参数
            if(isset($_REQUEST['route'])){
                $uri = str_replace('?/', '', $_REQUEST['route']);
                unset($_REQUEST['route']);
            }else{
                $uri = Request::getInstance()->query_string();
            }
            //http://local.dress/?keyword=dfds 这种要修复
            //去掉左侧的空格
            $uri = ltrim($uri, '/');
            //伪静态的URL，参数前面一般是有"&"，如：local.dress/?news&keyword=dfds，很少有根目录下直接跟着参数的情况
            $pos = strpos($uri, '&');
            if($pos === false){
                if(strpos($uri,'=')){
                    if(! empty($_GET)){
                        foreach ($_GET as $k => $v){
                            $uri = str_replace("{$k}={$v}", '', $uri);
                        }
                    }
                }else{
                    $route = $uri;
                }
            }else{
                $route = substr($uri, 0, $pos);
            }
            $route = rtrim($route, '/');
        }
        //保存路由到上下文，URL重写要用
        context_set('app_current_route_uri', $route);
        return $route;
    }


    /**
     * 格式化路由参数
     *
     * @param $argNum
     * @param  array  $args
     * @author lichunguang 153102250@qq.com
     * @since 2022/8/21 下午9:43
     */
    public function getDefaultRouteArgs($uri){
        if(empty($uri)){
            $argNum = 0;
            $args = [];
        }else {
            $args = explode('/', $uri);
            $argNum = count($args);
        }
        //默认的配置
        $moduleName = config('route.default_module');
        $controllerName = config('route.default_controller');
        $actionName = config('route.default_action');
        if($argNum > 0){
            $moduleName = $args[0];
        }
        if($argNum > 1){
            $controllerName = $args[1];
        }
        if($argNum > 2){
            $actionName = $args[2];
        }
        //检查后台路由
        if($moduleName == config('admin_dir')){
            $moduleName = 'admin';
        }

        //将模块 控制器  方法，保存到动态配置
        context_set('app_current_module', $moduleName);
        context_set('app_current_controller', $controllerName);
        context_set('app_current_action', $actionName);
//        context_set('app_current_route_args', []);
        //模块和控制器都要转为大驼峰，url中模块和控制器都用小写
        $moduleName = convertUnderline($moduleName, true);
        $controllerName = convertUnderline($controllerName, true);
        //转化成中间件处理
        //检查要登录的模块
        $middleware = [];
        if(in_array($moduleName, config('route.auth_module'))){
            //控制器是否免登, 非免登录，要设置中间件，兼容新的路由解析
            if(in_array($controllerName, config('route.auth_module_white_list')) === false){
                $middleware_config = config('route.auth_middleware');
                $middleware = $middleware_config[$moduleName] ?? [];
            }
        }

        return [
            'args' => [],
            'controller' => config('controller_namespace') ."{$moduleName}\\{$controllerName}Controller",
            'action'    => $actionName,
            'middleware'    => $middleware,
            'method'    => $actionName == 'save' ? 'post' : 'any'
        ];
    }


    //加载路由
    public function loadRoute(){
        $route_path = ROOT_PATH .'/routes/web.php';
        //加载路由缓存
        Route::init($route_path);
    }

    //加载配置文件
    public function loadConfig(){
        //加载环境变量配置
        Env::load();
        $config = ConfigCls::getInstance();
        //加载配置文件
        $config->load('app');
        $config->load('database');
        $config->load('route');
    }

    /**
     * 分发路由
     *
     *
     * @param  array  $routeArgs
     * @param $request
     * @return array|void
     * @author lichunguang 153102250@qq.com
     * @since 2022/8/26 下午10:54
     */
    public function dispatchRoute(array $routeArgs, $request){
        $controller = $routeArgs['controller'];
        $actionName = $routeArgs['action'];
        $flag = class_exists($controller);
        //对应的控制器存在，开始解析
        if($flag){
            try {
                $instance = new $controller();
                //将request实例到到控制器
                $instance->setRequest($request);
                if(method_exists($instance, $actionName)) {
                    //这里可以加返回值，统一处理输出
                    $instance->$actionName();
                    $this->terminate();
                }else{
                    throw new \Exception(ErrorEnums::ROUTE_NOT_FOUND);
                }
            }catch (\Exception $e){
                log_exception($e);
                $ajax = Request::getInstance()->postInt("_ajax", 0);
                if($ajax == 1){
                    return api_error($e->getMessage());
                }else {
                    //print_r($e->getFile() . ','. $e->getLine());
                    $this->errorPage('error', $e->getMessage());
                }
            }
        }else{
            $this->errorPage('404', '页面不存在，请检查路由配置');
        }

        return [];
    }

    /**
     * 跳转到错误页面
     *
     * @param  string  $type
     * @param  string  $message
     * @author lichunguang 153102250@qq.com
     * @since 2022/4/23 下午7:38
     */
    public function errorPage(string $type = 'error', string $message = ''){
        //echo '<h1>'. $message .'</h1>';
        $path = 'error_page';
        $data = [
            'message' => $message,
            'file' => '',
            'line'  => 0
        ];
        View::getInstance()->display($path, $data);
    }


    /**
     * 请求执行完毕的时候，执行关闭
     *xx
     * @author lichunguang 153102250@qq.com
     * @since 2022/4/23 下午3:13
     */
    public function terminate(){
        \App\Facades\Db::close();
    }

}

//$error_handler = set_error_handler("myErrorHandler");
